package com.cool.springcloud.myselfloadbalancer;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 实现自己的负载均衡算法
 *
 * @Author ifredomvip@gmail.com
 * @Date 2022/8/26 16:59
 * @Version 1.0.0
 * @Description
 **/
public class DefineMyselfLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private final String serviceId;
 
    public DefineMyselfLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.serviceId = serviceId;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);

        return supplier.get(request).next().map((serviceInstances) -> {

            Response<ServiceInstance> serviceInstanceResponse = this.processInstanceResponse(serviceInstances);
            if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
                ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
            }

            return this.processInstanceResponse(serviceInstances);
        });
    }

    /**
     * 定义我自己的算法
     * 如果是偶数那么就返回当前服务提供商实例，奇数那么就返回空，等同于当前服务实例不可用
     *
     * 所以测试结果是，只有当随机数2次都是偶数时，服务才可用
     */
    public Response<ServiceInstance> processInstanceResponse(List<ServiceInstance> serviceInstances) {

        // 由于本地测试，有且仅有两个服务： serverA,serverB,所以随机数为2
        int nextInt = ThreadLocalRandom.current().nextInt(2);
        ServiceInstance instance;
        if ((nextInt % 2) == 0) {
            instance = serviceInstances.get(nextInt);
        }else{
            return new EmptyResponse();
        }
        System.out.println("==============当前服务Id" + serviceId);

        return new DefaultResponse(instance);
    }
}